package collect;

import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Predicates.not;

import java.util.Collection;
import java.util.Collections;

import com.google.common.base.Predicate;

/**
 * Additional useful static Predicate methods to complement the Guava predicates.
 * @author Carl A. Pearson
 * @version 1.0
 */
public class Predicates {

	/**
	 * A Predicate on Collections; returns true when a test Collection is empty.
	 * <p>
	 * The resulting Predicate throws NullPointerException if provided a null value.
	 * @param <T> the type contained in the Collection
	 * @return {@code p(collection) == collection.isEmpty()}
	 */
	@SuppressWarnings("unchecked")
	public static <T> Predicate<Collection<T>> isEmpty() {
		return (Predicate<Collection<T>>)isEmpty;
	}
	
	/**
	 * Singleton implementation of {@link #isEmpty()} Predicate; since the predicate is non-
	 * type specific, only a single instance is need for all Collection types.
	 */
	private static final Predicate<? extends Collection<?>> isEmpty = new Predicate<Collection<Object>>() {
		@Override public boolean apply(Collection<Object> input) { return input.isEmpty(); }
	};

	/**
	 * Syntatic sugar wrapping {@code Predicates.not(Predicates.equals(target))}.
	 * @param <T> the type of the target.
	 * @param target the object for comparison
	 * @return {@code p(object) = (target==null && object != null) || !target.equals(object)}
	 */
	public static <T> Predicate<T> notEquals(T target) {
		return not(equalTo(target));   
	}
	
	/**
	 * A Predicate on Collections - returns true when a Collection's size == 1.
	 * <p>
	 * The resulting Predicate throws NullPointerException if provided a null value.
	 * @param <T> the type contained in the Collection
	 * @return p(collection) = collection.size() == 1
	 */
	@SuppressWarnings("unchecked")
	public static <T> Predicate<Collection<T>> isSingleton() {
		return (Predicate<Collection<T>>)isSingleton;
	}

	/**
	 * Singleton implementation of {@link #isSingleton()} Predicate; since the predicate is non-
	 * type specific, only a single instance is need for all Collection types.
	 */
	private static final Predicate<? extends Collection<?>> isSingleton = new Predicate<Collection<Object>>() {
		@Override public boolean apply(Collection<Object> input) { return input.size()==1; }
	};
	
	/**
	 * Converts {@link Collections#disjoint(Collection, Collection)} into a Predicate, using
	 * a target base collection.
	 * <p>
	 * Changes to the contents of {@code from} will change the behavior of the resulting
	 * Predicate.
	 * @param <T> the type contained in the Collection
	 * @param from the reference Collection
	 * @return {@code p(collection) = disjoint(from,collection)}
	 */
	public static <T> Predicate<Collection<T>> disjoint(final Collection<T> from) {
		return new Predicate<Collection<T>>(){
			@Override public boolean apply(Collection<T> input) {
				return Collections.disjoint(from,input);
			}
		};
	}
}
